package com.dream.refreshconfig;

import jakarta.annotation.PostConstruct;
import org.apache.commons.io.monitor.FileAlterationListener;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * @description: 配置文件监听器
 * @author: BrightLight
 * @create: 2025-05-28 22:06:23
 **/
public class ConfigFileChangeListener {

    protected final Log logger = LogFactory.getLog(ConfigFileChangeListener.class);

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private ContextRefresher contextRefresher;

    @PostConstruct
    public void init() throws Exception {
        // 获取配置文件路径
        Environment env = applicationContext.getEnvironment();
        String configPath = null;
        boolean useClassPath = false;

        boolean runningInJar = isRunningInJar();

        // 1. 尝试从spring.config.location加载配置文件
        if (configPath == null) {
            String location = env.getProperty("spring.config.location");
            if (location != null && !location.isEmpty()) {
                if (location.endsWith(".yml")) {
                    configPath = location;
                    logger.info("使用配置文件: " + configPath);
                } else if (location.endsWith(".properties")) {
                    configPath = location;
                    logger.info("使用配置文件: " + configPath);
                }
            }
        }

        // 2. 如果spring.config.location没有配置，使用默认的application.yml或application.properties
        if (configPath == null) {
            configPath = "classpath:application.yml";
            if (applicationContext.getResource(configPath).exists()) {
                logger.info("使用默认配置文件: " + configPath);
                useClassPath = true;
            } else {
                configPath = "classpath:application.properties";
                if (applicationContext.getResource(configPath).exists()) {
                    logger.info("使用默认配置文件: " + configPath);
                    useClassPath = true;
                } else {
                    logger.error("未找到配置文件");
                    configPath = null;
                }
            }
        }

        // 将内部配置文件复制到外部config目录下
        if (useClassPath && runningInJar) {
            logger.info("当前运行环境为Jar包，将内部配置文件复制到外部config目录下");
            // 创建外部配置目录
            Path externalConfigDir = Paths.get(System.getProperty("user.dir"), "config");
            logger.info("外部配置目录: " + externalConfigDir);
            if (!Files.exists(externalConfigDir)) {
                Files.createDirectories(externalConfigDir);
            }
            String fileName = configPath.substring("classpath:".length());
            Path externalConfigFile = externalConfigDir.resolve(fileName);
            if (!Files.exists(externalConfigFile)) {
                try (var inputStream = applicationContext.getResource(configPath).getInputStream()) {
                    Files.copy(inputStream, externalConfigFile);
                }
            }
            configPath = externalConfigFile.toString();
        }

        // 创建文件监听器
        FileAlterationMonitor monitor = new FileAlterationMonitor(1000);

        // 监听yml文件
        if (configPath != null) {
            File configFile;
            if (runningInJar) {
                configFile = new File(configPath);
            } else {
                Resource resource = applicationContext.getResource(configPath);
                configFile = resource.getFile();
            }
            FileAlterationObserver observer = new FileAlterationObserver(configFile.getParentFile());
            FileAlterationListener listener = new FileAlterationListenerAdaptor() {
                @Override
                public void onFileChange(File file) {
                    if (file.getName().equals(configFile.getName())) {
                        logger.info("配置文件 " + file.getAbsolutePath() + " 已更新，正在刷新配置...");
                        contextRefresher.refresh();
                        logger.info("配置刷新完成！");
                    }
                }
            };
            observer.addListener(listener);
            monitor.addObserver(observer);
            logger.info("开始监听配置文件: " + configFile.getAbsolutePath());
        }
        monitor.start();
        logger.info("配置文件监听器已启动");
    }

    /**
     * 判断应用是否在 JAR 包中运行
     * @return 如果在 JAR 包中运行返回 true，否则返回 false
     */
    private boolean isRunningInJar() {
        // 获取当前应用的主类
        String mainClass = System.getProperty("sun.java.command");
        if (mainClass == null) {
            return false;
        }

        // 检查是否包含 -jar 参数
        if (mainClass.contains("-jar")) {
            return true;
        }

        // 检查是否在 IDE 中运行
        String classPath = System.getProperty("java.class.path");
        if (classPath == null) {
            return false;
        }

        // 如果在 IDE 中运行，classpath 通常包含多个路径，且包含 IDE 特定的路径
        String[] paths = classPath.split(File.pathSeparator);
        boolean hasIdePath = false;
        boolean hasJarFile = false;

        for (String path : paths) {
            // 检查是否包含 IDE 特定的路径
            if (path.contains("idea_rt.jar") || 
                path.contains("eclipse") || 
                path.contains(".gradle") || 
                path.contains("target/classes")) {
                hasIdePath = true;
            }
            // 检查是否包含 jar 文件
            if (path.endsWith(".jar")) {
                hasJarFile = true;
            }
        }

        // 如果同时存在 IDE 路径和 jar 文件，说明是在 IDE 中运行
        return !(hasIdePath && hasJarFile);
    }

}